﻿// Shader-Based-Image-Processing (SBIP)
// http://code.google.com/p/sbip/
//
// Copyright © Frank Nagl, 2009-2011
// admin@franknagl.de
//
using SBIP.OptionsForms;

namespace SBIP
{
    using System;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.IO;
    using System.Windows.Forms;
    using OptionsForms.NonSBIP;

    /// <summary>
    /// Main form of the application ShaderBasedImageProcessor.
    /// </summary>
    public partial class ProcessorForm : Form
    {
        // determines, if old version is updateable
        private bool isUpdateable;
        private string optionalStartFileName;
        private GroupBox options;
        /// <summary>
        /// Factor for multiplying cursor's X-coordinate at <see cref="panel"/> 
        /// to get correct image pixel coordinate.
        /// </summary>
        private float scaleCursorX;
        /// <summary>
        /// Factor for multiplying cursor's Y-coordinate at <see cref="panel"/> 
        /// to get correct image pixel coordinate.
        /// </summary>
        private float scaleCursorY;
        private readonly GroupBox videoOptions;
        private readonly VideoForm videoForm;        

        /// <summary>The SBIP processor, which manages all image processing 
        /// routines.</summary>
        public Processor Processor { get; protected set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="ProcessorForm"/> class.
        /// </summary>
        /// <param name="useUpdater">If set to <c>true</c> the autoupdater is 
        /// used.</param>
        protected ProcessorForm(bool useUpdater)
        {
            InitializeComponent();
            Processor = new Processor();
            options = new GroupBox();
            videoForm = new VideoForm(Processor);
            videoOptions = videoForm.Options;
            videoOptions.Enabled = false;
            Controls.Add(videoOptions);
            Processor.OnDeviceLostEvent += HandleDeviceLost;
            if (!useUpdater)
            {
                return;
            }

            AutoUpdater updater = new AutoUpdater(
                Program.OptionsFile,
                "http://franknagl.de/updates/SBIP/update.csv",
                Program.ProgramPath,
                Program.ProgramExe);
            updater.OnCheckUpdateEvent += UpdateAvailable;
            updater.CheckUpdateAsThread();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ProcessorForm"/> class.
        /// </summary>
        public ProcessorForm() 
            : this(true)
        {            
            Initialize(null);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ProcessorForm"/> class.
        /// </summary>
        /// <param name="filename">The passed image or video filename.</param>
        public ProcessorForm(string filename)
            : this(true)
        {
            Initialize(filename);
        }

        /// <summary>Routine for updating each frame.</summary>
        /// <param name="firstCall">If first call it has to be set to 
        /// <c>true</c>.</param>
        public void UpdateEachFrame(bool firstCall)
        {
            if (Processor.IsVideo)
            {
                videoForm.UpdateEachFrame(firstCall);
            }
        }

        #region private methods
        // 1.) All filters with needed HighDef profile will disabled, 
        // if only reach profile is supported.
        // 2.) All complex filters will disabled of videos.
        private void CheckForHiDefAndComplexFilters()
        {
            CannyToolStripMenuItem.Enabled = Processor.HighDef;
            Convolution5x5ToolStripMenuItem.Enabled = Processor.HighDef;
            NonSBIPFilterToolStripMenuItem.Enabled = !Processor.IsVideo;
        }

        private void HandleDeviceLost()
        {
            Processor.End();
            Initialize(optionalStartFileName);
        }

        private void InfoToolStripMenuItemClick(object sender, EventArgs e)
        {
            new InfoForm().Show(this);
        }

        private void Initialize(string filename)
        {
            if (filename == null)
            {
                videoOptions.Enabled = false;
                System.Resources.ResourceManager resource =
                    new System.Resources.ResourceManager("SBIP.Resource",
                    System.Reflection.Assembly.GetExecutingAssembly());
                // ReSharper disable AssignNullToNotNullAttribute
                Bitmap bitmap = 
                    new Bitmap((Bitmap)resource.GetObject("dom_erfurt"));
                // ReSharper restore AssignNullToNotNullAttribute

                options.Height = 100; // Hack, for SetPanelSize(..) method
                SetPanelSize(bitmap.Width, bitmap.Height);
                Processor.Begin(bitmap, panel);
                InitOptions(new OriginalForm(Processor).Options);                
            }
            else
            {
                Text = filename;
                optionalStartFileName = filename;
                FileInfo info = new FileInfo(filename);
                string ext = info.Extension.ToLower();

                switch (ext)
                {
                    case ".wmf":
                    case ".tiff":
                    case ".tif":
                    case ".gif":
                    case ".emf":
                    case ".png":
                    case ".bmp":
                    case ".jpeg":
                    case ".jpg":
                        LoadImage(filename);
                        break;
                    case ".xvid":
                    case ".dvx":
                    case ".divx":
                    case ".mp4":
                    case ".mpeg4":
                    case ".mpg":
                    case ".mpeg":
                    case ".avi":
                        LoadVideo(filename);
                        break;
                    default:
                        if (MessageBox.Show(
                            "This file type is not supported by SBIP.\n" + 
                            @"Open SBIP anyway?", 
                            @"Wrong file type", 
                            MessageBoxButtons.YesNo) == DialogResult.Yes)
                        {
                            if (Processor.HasBegun)
                            {
                                Processor.End();
                            }
                           Initialize(null);
                        }
                        else
                        {
                            Dispose();
                            return;                             
                        }
                        break;
                }                
            }

            CheckForHiDefAndComplexFilters();
        }

        private void InitOptions(GroupBox groupBox)
        {
            Point loc = options.Location;
            Controls.Remove(options);
            options = groupBox;
            options.Location = loc;
            Controls.Add(options);

            // setting postion of informations group box
            infos.Location = new Point(
                options.Location.X, options.Location.Y + options.Height + 10);
        }

        private void LoadImage(string imageFilename)
        {
            videoOptions.Enabled = false;
            optionalStartFileName = imageFilename;
            if (Processor.HasBegun)
                Processor.End();
            Bitmap bitmap = new Bitmap(imageFilename);
            SetPanelSize(bitmap.Width, bitmap.Height);
            Processor.Begin(bitmap, panel);
            InitOptions(new OriginalForm(Processor).Options);
        }

        private void LoadVideo(string videoFilename)
        {
            optionalStartFileName = videoFilename;
            videoOptions.Enabled = true;
            try
            {
                if (Processor.HasBegun)
                    Processor.End();
                Size videoSize =
                    DirectShowLib.VideoPlayer.GetVideoSize(
                    videoFilename);
                SetPanelSize(videoSize.Width, videoSize.Height);

                Processor.Begin(videoFilename, panel);
                UpdateEachFrame(true);
                InitOptions(new OriginalForm(Processor).Options);
            }
            catch (Exception e)
            {
                //Console.WriteLine("ProcForm LoadVideo() in catch Zweig:");
                //Console.WriteLine(e.Message);
                //Console.WriteLine("\n source: " + e.Source);
                //Console.WriteLine("\n Stack: " + e.StackTrace);
                optionalStartFileName = null;
                videoOptions.Enabled = false;
                //throw new Exception
                //    ("This video is not playable. Maybe codec is missing.");
                MessageBox.Show(
                    @"This video is not playable. Maybe codec is missing. " +
                    e.Message,
                    @"Video is not playable.",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error);
                HandleDeviceLost();
            }
        }

        private void SetPanelSize(int w, int h)
        {
            const int optionsWidth = 390;
            Location = new Point(0, 0);
            Size maxSize = new Size(
                    SystemInformation.PrimaryMonitorSize.Width,
                    SystemInformation.PrimaryMonitorSize.Height - 45);
            int moniW = SystemInformation.PrimaryMonitorSize.Width - optionsWidth;
            int moniH = SystemInformation.PrimaryMonitorSize.Height - 240;
            int addWidthForSlider = 0; // slider needs width of 450

            if (moniW < w || moniH < h)
            {
                bool wLonger = (w / (float)h) > (moniW / (float)moniH);
                if (wLonger)
                {
                    moniH = h * moniW / w;
                    maxSize.Height = moniH + 170 + panel.Location.Y;
                }
                else
                {
                    moniW = w * moniH / h;
                    maxSize.Width = moniW + optionsWidth + panel.Location.X;
                }
                panel.Size = new Size(moniW, moniH);
                Size = maxSize;
            }
            else
            {
                panel.Size = new Size(w, h);
                Size = new Size(
                    w + optionsWidth + panel.Location.X,
                    h + 170 + panel.Location.Y);
            }

            // check, if windows is high enough, so complete options are visible
            if (panel.Size.Height < options.Height + 50 + panel.Location.Y)
            {
                Height = options.Height + 200 + panel.Location.Y;
                // avoid over height, when very small resolution
                Height = Math.Min(
                    Height, SystemInformation.PrimaryMonitorSize.Height - 45);
            }

            // check, if windows is width enough, so complete video options are visible
            if (panel.Size.Width < 450)
            {
                addWidthForSlider = 450 - panel.Size.Width;
                Width = Width + addWidthForSlider;//options.Height + optionsWidth + panel.Location.X;
            }

            options.Location = new Point(//832, 27);
                panel.Size.Width + addWidthForSlider + 30,
                30);

            videoOptions.Location = new Point(
                panel.Location.X, panel.Location.Y + panel.Size.Height + 20);

            videoOptions.Width = panel.Width + addWidthForSlider;

            // setting image informations
            scaleCursorX = w / (float)panel.Size.Width;
            scaleCursorY = h / (float)panel.Size.Height;
            lbWidth.Text = w.ToString();
            lbHeight.Text = h.ToString();
        }

        private void UpdateAvailable(bool updateable)
        {
            UpdateToolStripMenuItem.Visible = true;
            isUpdateable = updateable;
        }                

        #region Open-Save-Close-Update GUI events

        private void ProcessorFormFormClosing(object sender, FormClosingEventArgs e)
        {
            Processor.End();
        }

        private void SaveAsToolStripMenuItemClick(object sender, EventArgs e)
        {
            //saveFileDialog.InitialDirectory = "c:\\";
            saveFileDialog.Filter =
                @"PNG (*.png)|*.png|JPEG Files(*.jpg;*.jpeg)|*.jpg;*.jpeg|BMP (*.bmp)|*.bmp|" +
                @"TIFF Files(*.tif;*.tiff)|*.tif;*.tiff|GIF (*.gif)|*.gif|EMF (*.emf)|*.emf|" +
                @"WMF (*.wmf)|*.wmf|" +
                @"8 Bit Grayscale PNG Files(*.png)|*.png|" +
                @"8 Bit Grayscale BMP Files(*.bmp)|*.bmp";
                //@"8 Bit Indexed Color PNG Files(*.png)|*.png|" +
                //@"8 Bit Indexed Color BMP Files(*.bmp)|*.bmp|" +
                //@"8 Bit Colored Canny Edges PNG Files(*.png)|*.png|" +
                //@"8 Bit Colored Canny Edges BMP Files(*.bmp)|*.bmp";
            if (saveFileDialog.ShowDialog() != DialogResult.OK)
            {
                return;
            }
            // check, if user wants to save as 8 bit image or not
            Bitmap image = saveFileDialog.FilterIndex > 7 ? 
                           Processor.RenderToBitmap8Bpp() : 
                           Processor.RenderToBitmap();                

            switch (saveFileDialog.FilterIndex)
            {
                case 1: image.Save(saveFileDialog.FileName, ImageFormat.Png); break;
                case 2: JpegEncoder.SaveJpeg(saveFileDialog.FileName, image, 100);//100% quality
                    break;
                case 3: image.Save(saveFileDialog.FileName, ImageFormat.Bmp); break;
                case 4: image.Save(saveFileDialog.FileName, ImageFormat.Tiff); break;
                case 5: image.Save(saveFileDialog.FileName, ImageFormat.Gif); break;
                case 6: image.Save(saveFileDialog.FileName, ImageFormat.Emf); break;
                case 7: image.Save(saveFileDialog.FileName, ImageFormat.Wmf); break;

                // 8 bit grayscale images
                case 8: image.Save(saveFileDialog.FileName, ImageFormat.Png); break;                    
                case 9: image.Save(saveFileDialog.FileName, ImageFormat.Bmp); break;

                //// 8 bit indexed color images
                //case 10:
                //    Helper.ImageConverter.SetColorPalette(image);  
                //    image.Save(saveFileDialog.FileName, ImageFormat.Png); break;
                //case 11:
                //    Helper.ImageConverter.SetColorPalette(image);
                //    image.Save(saveFileDialog.FileName, ImageFormat.Bmp); break;

                //// 8 bit colored canny edges images
                //case 12:
                //    Helper.ImageConverter.SetCannyColorPalette(image);  
                //    image.Save(saveFileDialog.FileName, ImageFormat.Png); break;
                //case 13:
                //    Helper.ImageConverter.SetCannyColorPalette(image);  
                //    image.Save(saveFileDialog.FileName, ImageFormat.Bmp); break;
            }
        }

        private void TsItmOpenImageClick(object sender, EventArgs e)
        {
            openFileDialog.Filter = 
                @"Image Files(*.jpg;*.jpeg;*.bmp;*.png;*.emf;*.gif;*.tif;*.tiff;" +
                @"*.wmf)|*.jpg;*.jpeg;*.bmp;*.png;*.emf;*.gif;*.tif;*.tiff;*.wmf";
            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                LoadImage(openFileDialog.FileName);
            }
        }

        private void TsItmOpenVideoClick(object sender, EventArgs e)
        {
            openFileDialog.Filter =
                @"Video Files(*.avi;*.wmv)|*.avi;*.wmv";
            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                LoadVideo(openFileDialog.FileName);
            }
        }

        private void UpdateToolStripMenuItemClick(object sender, EventArgs e)
        {
            if (isUpdateable)
            {
                if (MessageBox.Show(
                    @"New version available. Do you want to update (recommended)?",
                    @"New version available :-)",
                    MessageBoxButtons.YesNo,
                    MessageBoxIcon.Question) == DialogResult.Yes)
                {
                    VistaSecurity.RestartElevatedForUpdate();
                }
            }
            else
            {
                if (MessageBox.Show(
                    @"New version available. Do you want to download it from SBIP website (recommended)?",
                    @"New version available :-)",
                    MessageBoxButtons.YesNo,
                    MessageBoxIcon.Question) == DialogResult.Yes)
                {
                    System.Diagnostics.Process.Start("http://code.google.com/p/sbip/");
                }
            }
        }

        private void QuitToolStripMenuItemClick(object sender, EventArgs e)
        {
            Close();
        }
        
        #endregion Open-Save-Close-Update GUI events

        #region Filter ToolStrip MenuItem Events
        private void NoFilterToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new OriginalForm(Processor).Options);
        }

        private void CannyToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new CannyForm(Processor).Options);
        }

        private void Convolution3X3ToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new Convolution3x3Form(Processor).Options);
        }

        private void Convolution5X5ToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new Convolution5x5Form(Processor).Options);
        }

        private void ChessboardToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new ChessboardForm(Processor).Options);
        }

        private void ExtractChannelToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new ExtractChannelForm(Processor).Options);
        }

        private void InvertToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new InvertForm(Processor).Options);
        }

        private void GaussianBlurToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new GaussianBlurForm(Processor).Options);
        }

        private void GrayscaleToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new GrayscaleForm(Processor).Options);
        }

        private void LaplaceToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new LaplaceForm(Processor).Options);
        }

        private void LightingToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new LightingForm(Processor).Options);
        }

        private void PosterizationToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new PosterizationForm(Processor).Options);
        }

        private void RotateChannelToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new RotateChannelsForm(Processor).Options);
        }

        private void SepiaToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new SepiaForm(Processor).Options);
        }

        private void SobelToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new SobelForm(Processor).Options);
        }

        private void SquareChannelToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new SquareChannelForm(Processor).Options);
        }

        private void ThresholdToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new ThresholdForm(Processor).Options);
        }

        private void ThresholdRgbToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new ThresholdRGBForm(Processor).Options);
        }

        private void VoronoiDiagramToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new VoronoiDiagramForm(
                Processor, 
                panel, 
                scaleCursorX, 
                scaleCursorY).Options);
        }

        private void WhiteContentToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new WhiteContentForm(Processor).Options);
        }


        private void MeanShiftToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new MeanShiftForm(Processor).Options);
        }
        #endregion Filter ToolStrip MenuItem Events  
      

        #region NonSBIP Filters ToolStrip MenuItem Events

        private void ColoredCannyEdgeDetectorToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new NSColoredCannyForm(Processor).Options);
        }

        private void ColorEliminatedRegionGrowingToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new NSColorEliminatedRegionGrowingForm(Processor).Options);
        }

        private void ConGrapLightToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new NSConGrapLightForm(Processor).Options);
        }

        private void ConTrapContourTracerToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new NSConGrapForm(Processor).Options);
        }

        private void KeyPointBasedFloodFillToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new NSKeyPointAndColorBasedSegmentationForm(Processor).Options);
        }

        private void LuvColorbasedSegmentationToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new NSLuvColorSegmentationForm(Processor).Options);
        }

        private void SimpleColorSegmentationToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new NSSimpleColorSegmentationForm(Processor).Options);
        }

        private void SimpleDilatationToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new NSSimpleDilatationForm(Processor).Options);
        }

        private void SimpleLuvColorbasedSegmentationToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new NSSimpleLuvColorSegmentationForm(Processor).Options);
        }

        private void SplitColorSpaceChannelsToolStripMenuItemClick(object sender, EventArgs e)
        {
            InitOptions(new NSSplitColorSpaceChannelsForm(Processor).Options);
        }

        #endregion NonSBIP Filters ToolStrip MenuItem Events

        #region Drag and drop and mouse events
        private void PanelMouseMove(object sender, MouseEventArgs e)
        {
            int mouseX = panel.PointToClient(Cursor.Position).X;
            int mouseY = panel.PointToClient(Cursor.Position).Y;
            lbX.Text = (Math.Round(mouseX * scaleCursorX + 0.5f)).ToString();
            lbY.Text = (Math.Round(mouseY * scaleCursorY + 0.5f)).ToString();
        }

        private void ProcessorFormDragDrop(object sender, DragEventArgs dea)
        {
            if (dea.Data.GetDataPresent(DataFormats.FileDrop))
            {
                string[] files = (string[])dea.Data.GetData(DataFormats.FileDrop);
                Initialize(files[0]); // only first element of drag objects
            }
        }

        private void ProcessorFormDragOver(object sender, DragEventArgs dea)
        {
            if (dea.Data.GetDataPresent(DataFormats.FileDrop))
                dea.Effect = DragDropEffects.Move;
        }
        #endregion Drag and drop and mouse events

        #endregion private methods
    }
}
